HERRAMIENTAS BÁSICAS PARA IMPORTAR, TRANSFORMAR Y VISUALIZAR DATOS

Maialen Iturbide

miturbide@ifca.unican.es

manualRdatascience

manualRdatascience

http://r4ds.had.co.nz/index.html


CONTENIDOS

Visualizar

Transformar

Importar/Exportar

Ordenar

Caso de estudio


ANTES DE EMPEZAR…UNA INTRODUCCIÓN A KAGGLE

Kaggle es una plataforma Web que reune a la mayor comunidad de data sciencetists del mundo: https://www.kaggle.com/

Kaggle ofrece:

  • Competiciones de Machine learning: Este fue el primer producto de Kaggle. Las Compañías publican problemas y los participantes compiten para construir el mejor algoritmo que dé solución al problema planteado.
  • Kernels: Son notebooks y scripts de python o de R que la comunidad comparte.
  • Datasets públicos: Los miembros de la comunidad comparten conjuntos de datos.
  • Kaggle Learn: ofrece cursos para aprender o mejorar en data science (e.g. python, machine learning, visualización de datos…)

VISUALIZAR DATOS

(Dataset de ejemplo: Iris de Fisher y mpg)

La visualización se trata de crear “plots” o gráficos informativos que ayuden a entender los datos.

En esta sección utilizaremos el dataset de Iris de Fisher (también llamado dataset de Iris de Anderson) (https://en.wikipedia.org/wiki/Iris_flower_data_set).

El conjunto de datos contiene 50 muestras de cada una de tres especies de Iris (Iris setosa, Iris virginica e Iris versicolor). Se midieron cuatro rasgos de cada muestra: El largo y ancho de los sépalos y el largo y ancho de los pétalos. Basado en la combinación de estos cuatro rasgos, Fisher desarrolló un modelo discriminante lineal para distinguir entre una especie y otra.

Este dataset se encuentra en formato csv en kaggle (https://www.kaggle.com/uciml/iris).

Lo podemos leer (una vez descargado) en nuestra sesión de R con la función read.csv o read.table:

iris.kagg <- read.table(file = "data/Iris.csv", sep = ",", header = TRUE)
str(iris.kagg)
## 'data.frame':    150 obs. of  6 variables:
##  $ Id           : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ SepalLengthCm: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ SepalWidthCm : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ PetalLengthCm: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ PetalWidthCm : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species      : Factor w/ 3 levels "Iris-setosa",..: 1 1 1 1 1 1 1 1 1 1 ...

Este dataset también se encuentra disponible en el paquete datasets de R:

library(help = "datasets")
?iris

Podemos ver la estructura de iris con la función str():

str(iris)
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

Fijándonos en la estructura de ambos data.frames (objetos iris.kagg e iris), vemos que la versión de iris de kaggle tiene una variable más (ID).

La función de visualización básica de R es plot (?plot para acceder a la ayuda).

plot(iris$Sepal.Length, iris$Sepal.Width, 
main="Edgar Anderson's Iris Data")

Visualicemos por ejemplo esta misma gráfica pero diferenciando con colores las diferentes especies de Iris. Para ello realizaremos una pequeña transformación de los datos en los pasos que se muestran a continuación.

La función levels() nos devuelve las especies que recoge el conjunto de datos:

levels(iris$Species)
## [1] "setosa"     "versicolor" "virginica"
# Extraigo los datos que corresponden únicamente a la especie de Iris setosa
ind <- which(iris$Species == "setosa")
str(iris[ind, ])
## 'data.frame':    50 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
# La función `subset` nos permite hacer lo mismo en un solo paso
setosa <- subset(iris, Species == "setosa")
str(setosa)
## 'data.frame':    50 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

Si aplicamos subset() para cada una de las especies, mediante las funciones plot y points podemos diferenciarlas en un mismo gráfico:

versicolor <- subset(iris, Species == "versicolor")
virginica <- subset(iris, Species == "virginica")
plot(setosa$Sepal.Length, setosa$Sepal.Width, xlim = c(4, 8), ylim = c(2, 4.5))
points(virginica$Sepal.Length, virginica$Sepal.Width, col = "blue")
points(versicolor$Sepal.Length, versicolor$Sepal.Width, col = "red")

Para este caso concreto, la transformación de arriba no es necesaria, ya que podemos pasarle al argumento col la variable Species:

plot(iris$Sepal.Length, iris$Sepal.Width, col = iris$Species)

Existen librerías más potentes para la visualización de datos, por ejemplo, ggplot o lattice:

install.packages("lattice")
## Installing package into '/home/maialen/R/x86_64-pc-linux-gnu-library/3.5'
## (as 'lib' is unspecified)
library(lattice)
xyplot(Sepal.Width~Sepal.Length, data = iris)

xyplot(Sepal.Width~Sepal.Length|Species, data = iris)

xyplot(Sepal.Width~Sepal.Length, group = Species, data = iris, auto.key = TRUE)

xyplot(Petal.Width~Petal.Length, group = Species, data = iris, 
       auto.key = TRUE, 
       col = c("orange", "blue", "green"))

cloud(Petal.Width ~ Petal.Length * Sepal.Width, group = Species, data = iris, auto.key = TRUE)

splom(~iris[1:4], groups = Species, data = iris)

bwplot(Petal.Length~Species, data = iris)

El paquete tidyverse

Datasets de ejemplo: Iris de Fisher y mpg

#install.packages("tidyverse")
x <- c("ggplot2","dplyr","tidyr","readr","purrr","tibble","stringr","forcats")
install.packages(x)
#library(tidyverse)
library("ggplot2")
library("dplyr")
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library("tidyr")
library("readr")
library("purrr")
library("tibble")
library("stringr")
library("forcats")

La librería tidyverse carga a su vez las siguientes librerías o paquetes: * ggplot2, para visualizar datos. * dplyr, para manipular datos. * tidyr, para ordenar datos. * readr, para importar datos. * purrr, para programar funciones. * tibble, para tibbles, una versión moderna de los data.frames. * stringr, para cadenas de caracteres (character strings). * forcats, para factores.

En el siguiente ejemplo utilizamos ggplot() (paquete ggplot2) para visualizar los datos de iris.

Se comienza una gráfica con la función ggplot(), que crea un sistema de coordenadas al que se pueden agregar capas con el operador +. ggplot(data = mpg) crea un gráfico vacío y se completa, en este ejemplo, con otra capa de puntos, la generada por la función geom_point:

ggplot(data = iris) +
geom_point(mapping = aes(x = Petal.Width, y = Petal.Length))

ggplot(data = iris, aes(Species, Petal.Length)) +
geom_boxplot()

# tenemos la opción de convertir el data.frame a tibble
iris <- as_tibble(iris)

De aquí en adelante utilizaremos el dataset mpg incluido en el paquete ggplot2:

data(package = "ggplot2")

Para asociar un parámetro estético a una variable añadimos el nombre de ese parámetro (o argumento) en aes().

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, color = class)) 

Los colores revelan que muchos de los puntos inusuales (rojo) son coches de dos plazas. A primeras, nadie diría que un coche de este tipo es híbrido… ¡son coches deportivos! que tienen motores grandes como los SUV y las Pickup, pero son pequeños, lo que mejora su consumo de combustible.

ggplot(mpg, aes(x = displ, y = hwy, color = class, shape= cty > 20)) +
geom_point()

Otra estrategia útil para diferenciar grupos de puntos es utilizar facet_wrap() como una capa de ggplot más.

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) + 
  facet_wrap(~class, nrow = 2)

Transformar datos

(Dataset de ejemplo: flights)

La visualización es una herramienta importante para la generación de información, pero es raro obtener los datos en la forma en la que se necesita. A menudo, habrá que crear nuevas variables o resúmenes, o tal vez solo cambiar el nombre de las variables o reordenar las observaciones…. para que sea más fácil trabajar con los datos.

Continuando con el uso de los paquetes de tidyverse, utilizaremos el paquete dplyr para transformar el dataset flights (flights departing New York City in 2013) que encontraréis en el subdirectorio “data” como un archivo de R (.rda). Para cargar flights en nuestra sesión utilizamos la función load.

load("data/flights.rda")
str(flights)
## Classes 'tbl_df', 'tbl' and 'data.frame':    336776 obs. of  19 variables:
##  $ year          : int  2013 2013 2013 2013 2013 2013 2013 2013 2013 2013 ...
##  $ month         : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ day           : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ dep_time      : int  517 533 542 544 554 554 555 557 557 558 ...
##  $ sched_dep_time: int  515 529 540 545 600 558 600 600 600 600 ...
##  $ dep_delay     : num  2 4 2 -1 -6 -4 -5 -3 -3 -2 ...
##  $ arr_time      : int  830 850 923 1004 812 740 913 709 838 753 ...
##  $ sched_arr_time: int  819 830 850 1022 837 728 854 723 846 745 ...
##  $ arr_delay     : num  11 20 33 -18 -25 12 19 -14 -8 8 ...
##  $ carrier       : chr  "UA" "UA" "AA" "B6" ...
##  $ flight        : int  1545 1714 1141 725 461 1696 507 5708 79 301 ...
##  $ tailnum       : chr  "N14228" "N24211" "N619AA" "N804JB" ...
##  $ origin        : chr  "EWR" "LGA" "JFK" "JFK" ...
##  $ dest          : chr  "IAH" "IAH" "MIA" "BQN" ...
##  $ air_time      : num  227 227 160 183 116 150 158 53 140 138 ...
##  $ distance      : num  1400 1416 1089 1576 762 ...
##  $ hour          : num  5 5 5 5 6 5 6 6 6 6 ...
##  $ minute        : num  15 29 40 45 0 58 0 0 0 0 ...
##  $ time_hour     : POSIXct, format: "2013-01-01 05:00:00" "2013-01-01 05:00:00" ...

Las cinco funciones clave de dplyr que te permiten resolver la gran mayoría de los problemas de manipulación de datos:

  • filter(), Extraer subconjuntos de datos en función de sus valores.
  • arrange(), Reordenar filas.
  • select(), Extraer variables por su nombre.
  • mutate(), Crear nuevas variables con funciones de las variables existentes.
  • summarise(), Collapse many values down to a single summary.

Filtrar filas

Mediante el uso de los operadores comparativos: >, >=, <, <=, != (no igual), == (igual)

jan1 <- filter(flights, month == 1, day == 1)
str(jan1)
## Classes 'tbl_df', 'tbl' and 'data.frame':    842 obs. of  19 variables:
##  $ year          : int  2013 2013 2013 2013 2013 2013 2013 2013 2013 2013 ...
##  $ month         : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ day           : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ dep_time      : int  517 533 542 544 554 554 555 557 557 558 ...
##  $ sched_dep_time: int  515 529 540 545 600 558 600 600 600 600 ...
##  $ dep_delay     : num  2 4 2 -1 -6 -4 -5 -3 -3 -2 ...
##  $ arr_time      : int  830 850 923 1004 812 740 913 709 838 753 ...
##  $ sched_arr_time: int  819 830 850 1022 837 728 854 723 846 745 ...
##  $ arr_delay     : num  11 20 33 -18 -25 12 19 -14 -8 8 ...
##  $ carrier       : chr  "UA" "UA" "AA" "B6" ...
##  $ flight        : int  1545 1714 1141 725 461 1696 507 5708 79 301 ...
##  $ tailnum       : chr  "N14228" "N24211" "N619AA" "N804JB" ...
##  $ origin        : chr  "EWR" "LGA" "JFK" "JFK" ...
##  $ dest          : chr  "IAH" "IAH" "MIA" "BQN" ...
##  $ air_time      : num  227 227 160 183 116 150 158 53 140 138 ...
##  $ distance      : num  1400 1416 1089 1576 762 ...
##  $ hour          : num  5 5 5 5 6 5 6 6 6 6 ...
##  $ minute        : num  15 29 40 45 0 58 0 0 0 0 ...
##  $ time_hour     : POSIXct, format: "2013-01-01 05:00:00" "2013-01-01 05:00:00" ...
day1 <- filter(flights, month != 2, day == 1)
ggplot(day1, aes(x = month, y = dep_delay, group = month)) +
geom_boxplot(width=0.6, na.rm = TRUE)

month2 <- filter(flights, month == 5 | month == 7, day == 1)
ggplot(month2, aes(x = month, y = dep_delay, group = month)) +
geom_boxplot(width=0.6, na.rm = TRUE)

Ordenar filas

bydate <- arrange(flights, year, month, day)
bydelay <- arrange(flights, desc(dep_delay))
ggplot(flights) +
geom_point(aes(1:nrow(flights), dep_delay)) +
geom_point(aes(1:nrow(flights), bydelay$dep_delay), color = "red")
## Warning: Removed 8255 rows containing missing values (geom_point).

## Warning: Removed 8255 rows containing missing values (geom_point).

Seleccionar columnas

# Seleccionar por el nombre de la columna
print(select(flights, year, month, day))
## # A tibble: 336,776 x 3
##     year month   day
##    <int> <int> <int>
##  1  2013     1     1
##  2  2013     1     1
##  3  2013     1     1
##  4  2013     1     1
##  5  2013     1     1
##  6  2013     1     1
##  7  2013     1     1
##  8  2013     1     1
##  9  2013     1     1
## 10  2013     1     1
## # … with 336,766 more rows
# Seleccionar todas las columnas entre "year" y "day"
print(select(flights, year:day))
## # A tibble: 336,776 x 3
##     year month   day
##    <int> <int> <int>
##  1  2013     1     1
##  2  2013     1     1
##  3  2013     1     1
##  4  2013     1     1
##  5  2013     1     1
##  6  2013     1     1
##  7  2013     1     1
##  8  2013     1     1
##  9  2013     1     1
## 10  2013     1     1
## # … with 336,766 more rows
# Seleccionar todas las columnas excepto las que están entre "year" y "day"
print(select(flights, -(year:day)))
## # A tibble: 336,776 x 16
##    dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
##       <int>          <int>     <dbl>    <int>          <int>     <dbl>
##  1      517            515         2      830            819        11
##  2      533            529         4      850            830        20
##  3      542            540         2      923            850        33
##  4      544            545        -1     1004           1022       -18
##  5      554            600        -6      812            837       -25
##  6      554            558        -4      740            728        12
##  7      555            600        -5      913            854        19
##  8      557            600        -3      709            723       -14
##  9      557            600        -3      838            846        -8
## 10      558            600        -2      753            745         8
## # … with 336,766 more rows, and 10 more variables: carrier <chr>,
## #   flight <int>, tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>,
## #   distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

Hya una serie de funciones “helper” que pueden utilizarse dentro de la función select():

  • starts_with("abc")

  • ends_with("xyz")

  • contains("ijk")

  • matches("(.)\\1")

  • num_range("x", 1:3)

flight_times <- select(flights, ends_with("time"))
str(flight_times)
## Classes 'tbl_df', 'tbl' and 'data.frame':    336776 obs. of  5 variables:
##  $ dep_time      : int  517 533 542 544 554 554 555 557 557 558 ...
##  $ sched_dep_time: int  515 529 540 545 600 558 600 600 600 600 ...
##  $ arr_time      : int  830 850 923 1004 812 740 913 709 838 753 ...
##  $ sched_arr_time: int  819 830 850 1022 837 728 854 723 846 745 ...
##  $ air_time      : num  227 227 160 183 116 150 158 53 140 138 ...

Otra función “helper” muy útil es everything()

df <- select(flights, time_hour, air_time, everything())
str(df)
## Classes 'tbl_df', 'tbl' and 'data.frame':    336776 obs. of  19 variables:
##  $ time_hour     : POSIXct, format: "2013-01-01 05:00:00" "2013-01-01 05:00:00" ...
##  $ air_time      : num  227 227 160 183 116 150 158 53 140 138 ...
##  $ year          : int  2013 2013 2013 2013 2013 2013 2013 2013 2013 2013 ...
##  $ month         : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ day           : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ dep_time      : int  517 533 542 544 554 554 555 557 557 558 ...
##  $ sched_dep_time: int  515 529 540 545 600 558 600 600 600 600 ...
##  $ dep_delay     : num  2 4 2 -1 -6 -4 -5 -3 -3 -2 ...
##  $ arr_time      : int  830 850 923 1004 812 740 913 709 838 753 ...
##  $ sched_arr_time: int  819 830 850 1022 837 728 854 723 846 745 ...
##  $ arr_delay     : num  11 20 33 -18 -25 12 19 -14 -8 8 ...
##  $ carrier       : chr  "UA" "UA" "AA" "B6" ...
##  $ flight        : int  1545 1714 1141 725 461 1696 507 5708 79 301 ...
##  $ tailnum       : chr  "N14228" "N24211" "N619AA" "N804JB" ...
##  $ origin        : chr  "EWR" "LGA" "JFK" "JFK" ...
##  $ dest          : chr  "IAH" "IAH" "MIA" "BQN" ...
##  $ distance      : num  1400 1416 1089 1576 762 ...
##  $ hour          : num  5 5 5 5 6 5 6 6 6 6 ...
##  $ minute        : num  15 29 40 45 0 58 0 0 0 0 ...

Añadir nuevas variables

Además de seleccionar conjuntos de columnas existentes, a menudo es útil agregar nuevas columnas que son funciones de columnas existentes. Ese es el trabajo de mutate().

mutate() siempre agrega nuevas columnas al final de su conjunto de datos, por lo que utilizaremos un subconjunto de fights (usando select()) para que podamos ver las nuevas variables.

flights_sml <- select(flights, 
  year:day, 
  ends_with("delay"), 
  distance, 
  air_time
)

mutate(flights_sml,
  gain = dep_delay - arr_delay,
  speed = distance / air_time * 60
)
## # A tibble: 336,776 x 9
##     year month   day dep_delay arr_delay distance air_time  gain speed
##    <int> <int> <int>     <dbl>     <dbl>    <dbl>    <dbl> <dbl> <dbl>
##  1  2013     1     1         2        11     1400      227    -9  370.
##  2  2013     1     1         4        20     1416      227   -16  374.
##  3  2013     1     1         2        33     1089      160   -31  408.
##  4  2013     1     1        -1       -18     1576      183    17  517.
##  5  2013     1     1        -6       -25      762      116    19  394.
##  6  2013     1     1        -4        12      719      150   -16  288.
##  7  2013     1     1        -5        19     1065      158   -24  404.
##  8  2013     1     1        -3       -14      229       53    11  259.
##  9  2013     1     1        -3        -8      944      140     5  405.
## 10  2013     1     1        -2         8      733      138   -10  319.
## # … with 336,766 more rows

¿Qué ocurre con los NA del final?


IMPORTAR Y EXPORTAR (LEER Y ESCRIBIR) DATOS

(Dataset de ejemplo: Iris de Fisher y flights)

Trabajar con los datos proporcionados por los paquetes de R es una excelente manera de aprender las herramientas de data science, pero en algún momento necesitaréis comenzar a trabajar con vuestros propios datos.

Utilizaremos el paquete readr, que es también parte de tidyverse.

Leer datos

La mayoría de las funciones de lectura convierten archivos planos en data frames:

  • read_csv() lee archivos delimitados por comas, read_csv2() lee archivos separados por punto y coma, read_tsv() lee archivos delimitados por tabulaciones, y read_delim() lee archivos con cualquier delimitador.

  • read_fwf() lee archivos de ancho fijo. Puede especificar campos por su ancho con fwf_widths() o su posición con fwf_positions(). read_table() lee una variación común de archivos de ancho fijo donde las columnas están separadas por espacios en blanco.

  • read_log() reads Apache style log files (ver https://www.screamingfrog.co.uk/an-seos-guide-to-apache-log-files/)

iris <- read_csv("data/Iris.csv")
## Parsed with column specification:
## cols(
##   Id = col_double(),
##   SepalLengthCm = col_double(),
##   SepalWidthCm = col_double(),
##   PetalLengthCm = col_double(),
##   PetalWidthCm = col_double(),
##   Species = col_character()
## )
str(iris)
## Classes 'spec_tbl_df', 'tbl_df', 'tbl' and 'data.frame': 150 obs. of  6 variables:
##  $ Id           : num  1 2 3 4 5 6 7 8 9 10 ...
##  $ SepalLengthCm: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ SepalWidthCm : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ PetalLengthCm: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ PetalWidthCm : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species      : chr  "Iris-setosa" "Iris-setosa" "Iris-setosa" "Iris-setosa" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   Id = col_double(),
##   ..   SepalLengthCm = col_double(),
##   ..   SepalWidthCm = col_double(),
##   ..   PetalLengthCm = col_double(),
##   ..   PetalWidthCm = col_double(),
##   ..   Species = col_character()
##   .. )

También podemos pasar a read_csv un csv que generemos “al vuelo” en R:

read_csv("a,b,c
1,2,3
4,5,6")
## # A tibble: 2 x 3
##       a     b     c
##   <dbl> <dbl> <dbl>
## 1     1     2     3
## 2     4     5     6

A veces hay algunas líneas de metadatos en la parte superior del archivo: * El argumento skip = n se usa para omitir las primeras n líneas. * El argumento comment = "#" se usa para eliminar todas las líneas que comienzan con (por ejemplo #)

read_csv("The first line of metadata
  The second line of metadata
  x,y,z
  1,2,3", skip = 2)
## # A tibble: 1 x 3
##       x     y     z
##   <dbl> <dbl> <dbl>
## 1     1     2     3
read_csv("# A comment I want to skip
  x,y,z
  1,2,3", comment = "#")
## # A tibble: 1 x 3
##       x     y     z
##   <dbl> <dbl> <dbl>
## 1     1     2     3

Es posible que los datos no tengan nombres de columna. Puedes usar col_names = FALSE. (\n se puede usar para agregar una nueva línea).

read_csv("1,2,3\n4,5,6", col_names = FALSE)
## # A tibble: 2 x 3
##      X1    X2    X3
##   <dbl> <dbl> <dbl>
## 1     1     2     3
## 2     4     5     6

Puedes pasar a col_names un vector de caracteres que se usará como nombres de columna:

read_csv("1,2,3\n4,5,6", col_names = c("x", "y", "z"))
## # A tibble: 2 x 3
##       x     y     z
##   <dbl> <dbl> <dbl>
## 1     1     2     3
## 2     4     5     6

El argumento na se usa para especificar qué valor (o valores) del archivo son “missing values”:

read_csv("a,b,c\n1,2,.", na = ".")
## # A tibble: 1 x 3
##       a     b c    
##   <dbl> <dbl> <lgl>
## 1     1     2 NA

Las principales ventajas de read_csv frente a read.csv (de base de R) son: * Es más rápido. * Importa los datos a un objeto de clase tibble. De manera que… - No convierte los “character” a “factor” for defecto - No usa nombres de filas - No transforma los nombres de columna


Práctica 4

¿Qúe función de importación utilizarías para un archivo separado por “|” (e.g. “a|b|c1|2|3”)? Escribe un ejemplo a continuación:

Identifica qué está mal en los siguientes archivos CSV y corrígelo:

read_csv("a,b\n1,2,3\n4,5,6")
## Warning: 2 parsing failures.
## row col  expected    actual         file
##   1  -- 2 columns 3 columns literal data
##   2  -- 2 columns 3 columns literal data
## # A tibble: 2 x 2
##       a     b
##   <dbl> <dbl>
## 1     1     2
## 2     4     5
read_csv("a;b\n1;3")
## # A tibble: 1 x 1
##   `a;b`
##   <chr>
## 1 1;3

Escribir datos

readr incluye tres funciones útiles para escribir datos en disco: write_csv(), write_delim() write_tsv()


Práctica 5

Exporta el dataset flights al directorio que quieras (utiliza ?write_csv como ayuda).

# write_...

ORDENAR DATOS

(Datasets de ejemplo: table1, table2, table3, … y flights)

A veces nos tenemos que enfrentar a datasets desordenados y complejos. El paquete tidyr ofrece herramientas que ayudan a ordenar datos. Los datos que utilizaremos en esta sección se incluyen en el paquete tidyr (e.g. table1, table2, …)

table1
## # A tibble: 6 x 4
##   country      year  cases population
##   <chr>       <int>  <int>      <int>
## 1 Afghanistan  1999    745   19987071
## 2 Afghanistan  2000   2666   20595360
## 3 Brazil       1999  37737  172006362
## 4 Brazil       2000  80488  174504898
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583
table2
## # A tibble: 12 x 4
##    country      year type            count
##    <chr>       <int> <chr>           <int>
##  1 Afghanistan  1999 cases             745
##  2 Afghanistan  1999 population   19987071
##  3 Afghanistan  2000 cases            2666
##  4 Afghanistan  2000 population   20595360
##  5 Brazil       1999 cases           37737
##  6 Brazil       1999 population  172006362
##  7 Brazil       2000 cases           80488
##  8 Brazil       2000 population  174504898
##  9 China        1999 cases          212258
## 10 China        1999 population 1272915272
## 11 China        2000 cases          213766
## 12 China        2000 population 1280428583
table3
## # A tibble: 6 x 3
##   country      year rate             
## * <chr>       <int> <chr>            
## 1 Afghanistan  1999 745/19987071     
## 2 Afghanistan  2000 2666/20595360    
## 3 Brazil       1999 37737/172006362  
## 4 Brazil       2000 80488/174504898  
## 5 China        1999 212258/1272915272
## 6 China        2000 213766/1280428583
table4a
## # A tibble: 3 x 3
##   country     `1999` `2000`
## * <chr>        <int>  <int>
## 1 Afghanistan    745   2666
## 2 Brazil       37737  80488
## 3 China       212258 213766
table4b
## # A tibble: 3 x 3
##   country         `1999`     `2000`
## * <chr>            <int>      <int>
## 1 Afghanistan   19987071   20595360
## 2 Brazil       172006362  174504898
## 3 China       1272915272 1280428583

Todas estas tablas son representaciones del mismo conjunto de datos subyacente, pero no son igualmente fáciles de usar. Nuestro objetivo es obtener un conjunto de datos ordenado.

Hay tres reglas que cumple un CONJUNTO DE DATOS ORDENADO:

  • Cada variable debe tener su propia columna.
  • Cada observación debe tener su propia fila.
  • Cada valor debe tener su propia celda.

En este ejemplo, únicamente table1 está ordenada. Es la única representación donde cada columna es una variable.

# Añadimos la variable rate (rate per 10,000)
  mutate(table1, rate = cases / population * 10000)
## # A tibble: 6 x 5
##   country      year  cases population  rate
##   <chr>       <int>  <int>      <int> <dbl>
## 1 Afghanistan  1999    745   19987071 0.373
## 2 Afghanistan  2000   2666   20595360 1.29 
## 3 Brazil       1999  37737  172006362 2.19 
## 4 Brazil       2000  80488  174504898 4.61 
## 5 China        1999 212258 1272915272 1.67 
## 6 China        2000 213766 1280428583 1.67
  • La Función gather(): Un problema común es un conjunto de datos donde algunos de los nombres de columnas no son nombres de variables, sino valores de una variable, como ocurre en table4a y table4b.
table4a
## # A tibble: 3 x 3
##   country     `1999` `2000`
## * <chr>        <int>  <int>
## 1 Afghanistan    745   2666
## 2 Brazil       37737  80488
## 3 China       212258 213766
table4b
## # A tibble: 3 x 3
##   country         `1999`     `2000`
## * <chr>            <int>      <int>
## 1 Afghanistan   19987071   20595360
## 2 Brazil       172006362  174504898
## 3 China       1272915272 1280428583
tidy4a <- gather(table4a, `1999`, `2000`, key = "year", value = "cases")
tidy4a
## # A tibble: 6 x 3
##   country     year   cases
##   <chr>       <chr>  <int>
## 1 Afghanistan 1999     745
## 2 Brazil      1999   37737
## 3 China       1999  212258
## 4 Afghanistan 2000    2666
## 5 Brazil      2000   80488
## 6 China       2000  213766
tidy4b <- gather(table4b, `1999`, `2000`, key = "year", value = "population")
tidy4b
## # A tibble: 6 x 3
##   country     year  population
##   <chr>       <chr>      <int>
## 1 Afghanistan 1999    19987071
## 2 Brazil      1999   172006362
## 3 China       1999  1272915272
## 4 Afghanistan 2000    20595360
## 5 Brazil      2000   174504898
## 6 China       2000  1280428583

Utilizamos la función left_join() de dplyr para combinar table4a y table4b:

left_join(tidy4a, tidy4b)
## Joining, by = c("country", "year")
## # A tibble: 6 x 4
##   country     year   cases population
##   <chr>       <chr>  <int>      <int>
## 1 Afghanistan 1999     745   19987071
## 2 Brazil      1999   37737  172006362
## 3 China       1999  212258 1272915272
## 4 Afghanistan 2000    2666   20595360
## 5 Brazil      2000   80488  174504898
## 6 China       2000  213766 1280428583
  • La Función spread(). A veces una observación está dispersa en varias filas (e.g. table2, una observación es un país en un año, pero cada observación se distribuye en dos filas).
table2
## # A tibble: 12 x 4
##    country      year type            count
##    <chr>       <int> <chr>           <int>
##  1 Afghanistan  1999 cases             745
##  2 Afghanistan  1999 population   19987071
##  3 Afghanistan  2000 cases            2666
##  4 Afghanistan  2000 population   20595360
##  5 Brazil       1999 cases           37737
##  6 Brazil       1999 population  172006362
##  7 Brazil       2000 cases           80488
##  8 Brazil       2000 population  174504898
##  9 China        1999 cases          212258
## 10 China        1999 population 1272915272
## 11 China        2000 cases          213766
## 12 China        2000 population 1280428583
tidy2 <- spread(table2, key = type, value = count)
tidy2
## # A tibble: 6 x 4
##   country      year  cases population
##   <chr>       <int>  <int>      <int>
## 1 Afghanistan  1999    745   19987071
## 2 Afghanistan  2000   2666   20595360
## 3 Brazil       1999  37737  172006362
## 4 Brazil       2000  80488  174504898
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583
  • La función separate(). A veces podemos encontrarnos con una columna que contiene dos variables (e.g. table3, columna rate contiene las variables cases y population).
table3
## # A tibble: 6 x 3
##   country      year rate             
## * <chr>       <int> <chr>            
## 1 Afghanistan  1999 745/19987071     
## 2 Afghanistan  2000 2666/20595360    
## 3 Brazil       1999 37737/172006362  
## 4 Brazil       2000 80488/174504898  
## 5 China        1999 212258/1272915272
## 6 China        2000 213766/1280428583
separate(table3, rate, into = c("cases", "population"))
## # A tibble: 6 x 4
##   country      year cases  population
##   <chr>       <int> <chr>  <chr>     
## 1 Afghanistan  1999 745    19987071  
## 2 Afghanistan  2000 2666   20595360  
## 3 Brazil       1999 37737  172006362 
## 4 Brazil       2000 80488  174504898 
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583
tidy3 <- separate(table3, rate, into = c("cases", "population"), sep = "/")
  • La función unite(). Nos puede interesar combinar multiples variables en una única (e.g. para fechas)

Hasta ahora, hemos ido ejecutando cada proceso con líneas de código independientes, asignando cada resultado provisional a una nueva variable. Por lo general, se suele construir un “pipe” gradualmente con %>%.

Por ejemplo:

data(iris)
hello <- iris %>%
group_by(Species) %>%
  summarise(meanSL = mean(Sepal.Length), meanSW = mean(Sepal.Width), 
          meanPL = mean(Petal.Length), meanPW = mean(Petal.Width))
hello
## # A tibble: 3 x 5
##   Species    meanSL meanSW meanPL meanPW
##   <fct>       <dbl>  <dbl>  <dbl>  <dbl>
## 1 setosa       5.01   3.43   1.46  0.246
## 2 versicolor   5.94   2.77   4.26  1.33 
## 3 virginica    6.59   2.97   5.55  2.03
titanic <- read_csv("data/titanic.csv")
## Parsed with column specification:
## cols(
##   PassengerId = col_double(),
##   Survived = col_double(),
##   Pclass = col_double(),
##   Name = col_character(),
##   Sex = col_character(),
##   Age = col_double(),
##   SibSp = col_double(),
##   Parch = col_double(),
##   Ticket = col_character(),
##   Fare = col_double(),
##   Cabin = col_character(),
##   Embarked = col_character()
## )
str(titanic)
## Classes 'spec_tbl_df', 'tbl_df', 'tbl' and 'data.frame': 891 obs. of  12 variables:
##  $ PassengerId: num  1 2 3 4 5 6 7 8 9 10 ...
##  $ Survived   : num  0 1 1 1 0 0 0 0 1 1 ...
##  $ Pclass     : num  3 1 3 1 3 3 1 3 3 2 ...
##  $ Name       : chr  "Braund, Mr. Owen Harris" "Cumings, Mrs. John Bradley (Florence Briggs Thayer)" "Heikkinen, Miss. Laina" "Futrelle, Mrs. Jacques Heath (Lily May Peel)" ...
##  $ Sex        : chr  "male" "female" "female" "female" ...
##  $ Age        : num  22 38 26 35 35 NA 54 2 27 14 ...
##  $ SibSp      : num  1 1 0 1 0 0 0 3 0 1 ...
##  $ Parch      : num  0 0 0 0 0 0 0 1 2 0 ...
##  $ Ticket     : chr  "A/5 21171" "PC 17599" "STON/O2. 3101282" "113803" ...
##  $ Fare       : num  7.25 71.28 7.92 53.1 8.05 ...
##  $ Cabin      : chr  NA "C85" NA "C123" ...
##  $ Embarked   : chr  "S" "C" "S" "S" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   PassengerId = col_double(),
##   ..   Survived = col_double(),
##   ..   Pclass = col_double(),
##   ..   Name = col_character(),
##   ..   Sex = col_character(),
##   ..   Age = col_double(),
##   ..   SibSp = col_double(),
##   ..   Parch = col_double(),
##   ..   Ticket = col_character(),
##   ..   Fare = col_double(),
##   ..   Cabin = col_character(),
##   ..   Embarked = col_character()
##   .. )
titanic %>%

  select(-PassengerId, -Name, -Cabin, -Ticket) %>%

  mutate(Sex = fct_recode(Sex,
           "0" = "male",
           "1" = "female"),
         Embarked = fct_recode(Embarked,
           "1" = "S",
           "2" = "C",
           "3" = "Q")  

        ) %>%
 
  mutate(Sex = as.integer(Sex),
         Embarked = as.integer(Embarked),
         Pclass = as.integer(Pclass),
         Survived = as.integer(Survived)) %>%
  filter(complete.cases(.)) %>%
  cor() %>%
  abs() %>%
  levelplot(col.regions = c(gray.colors(5), rev(gray.colors(5))) , at = seq(-1, 1, 0.2))


CASO DE ESTUDIO

(Dataset de ejemplo: Kaggle ML and Data Science Survey, 2017)

1.- Consulta el “overview” del dataset “Kaggle ML and Data Science Survey, 2017” de Kaggle (https://www.kaggle.com/kaggle/kaggle-survey-2017)

2.- Descárgalo y léelo en R. ¿Un único csv o varios?

3.- Echa un vistazo a los scripts de R markdown (notebooks con extensión “.rmd”) disponibles (pestaña kernels).

4.- Puedes elegir el que más te interese. Este, https://www.kaggle.com/kumarhalake/kaggle-survey-2017-graphical-exploration, es apropiado para visualizar datos. Puedes encontrar otro donde muestren cómo transformar los datos, por ejemplo, https://www.kaggle.com/mrisdal/dealing-with-dirty-data-on-the-job, o cómo ordenarlos (tidy).

5.- Reproduce alguno de los gráficos y pasos que se muestran en los notebooks de kaggle.